package cn.slipi.admin.service.impl;

import cn.slipi.admin.common.exception.base.BizException;
import cn.slipi.admin.common.utils.DateUtil;
import cn.slipi.admin.common.utils.StrUtils;
import cn.slipi.admin.common.utils.UUIDUtil;
import cn.slipi.admin.common.utils.dozer.impl.ConvertBean;
import cn.slipi.admin.entity.CsmGrant;
import cn.slipi.admin.entity.CsmProject;
import cn.slipi.admin.entity.PdrApi;
import cn.slipi.admin.dao.PdrApiMapper;
import cn.slipi.admin.entity.PdrProject;
import cn.slipi.admin.service.CsmGrantService;
import cn.slipi.admin.service.CsmProjectService;
import cn.slipi.admin.service.PdrApiService;
import cn.slipi.admin.service.PdrProjectService;
import cn.slipi.admin.service.dto.in.pdrapi.GrantListDTO;
import cn.slipi.admin.service.dto.in.pdrapi.ListDTO;
import cn.slipi.admin.service.dto.in.pdrapi.NoGrantListDTO;
import cn.slipi.admin.service.dto.in.pdrapi.SaveDTO;
import cn.slipi.admin.service.dto.out.doc.ApiIn;
import cn.slipi.admin.service.dto.out.doc.ApiInfo;
import cn.slipi.admin.service.dto.out.doc.ApiOut;
import cn.slipi.admin.service.dto.out.doc.WriteDTO;
import com.alibaba.fastjson.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * <p>
 * 生产者项目（服务提供者） 服务实现类
 * </p>
 *
 * @author lee
 * @since 2020-12-01
 */
@Service
public class PdrApiServiceImpl extends ServiceImpl<PdrApiMapper, PdrApi> implements PdrApiService {
    @Autowired
    private ConvertBean convertBean;

    @Autowired
    private CsmGrantService csmGrantService;

    @Autowired
    private PdrProjectService projectService;

    @Autowired
    private CsmProjectService csmProjectService;


    @Override
    public IPage<PdrApi> getList(ListDTO dto) {
        IPage<PdrApi> page = new Page<>(dto.getPageNo(), dto.getPageSize());
        QueryWrapper<PdrApi> queryWrapper = new QueryWrapper<>();

        if (dto.getPublishStatus() != null) {
            if (dto.getPublishStatus().equals(0)) {
                queryWrapper.apply("key_update_time > publish_time");
            } else if (dto.getPublishStatus().equals(1)) {
                queryWrapper.apply("publish_time > key_update_time");
            }
        }

        if (dto.getPdrProjectId() != null) {
            queryWrapper.eq("pdr_project_id", dto.getPdrProjectId());
        }
        if (dto.getEnable() != null) {
            queryWrapper.eq("enable", dto.getEnable());
        }
        if (StrUtils.isNotBlank(dto.getCode())) {
            queryWrapper.like("code", dto.getCode());
        }
        if (StrUtils.isNotBlank(dto.getName())) {
            queryWrapper.like("name", dto.getName());
        }
        if (StrUtils.isNotBlank(dto.getQueryType())) {
            queryWrapper.eq("query_type", dto.getQueryType());
        }
        if (StrUtils.isNotBlank(dto.getPath())) {
            queryWrapper.like("path", dto.getPath());
        }
        if (StrUtils.isNotBlank(dto.getDescription())) {
            queryWrapper.like("description", dto.getDescription());
        }

        queryWrapper.eq("status", 1);
        queryWrapper.orderByDesc("updated_at");

        return this.baseMapper.selectPage(page, queryWrapper);
    }

    @Override
    public IPage<PdrApi> getNoGrantList(NoGrantListDTO dto) {
        IPage<PdrApi> page = new Page<>(dto.getPageNo(), dto.getPageSize());
        //获取已经授权给消费者的接口id
        List<CsmGrant> csmGrantList = this.csmGrantService.getGrantListByCsmId(dto.getCsmId());
        List<Integer> pdrApiIdList = csmGrantList.stream().map(CsmGrant::getPdrApiId).collect(Collectors.toList());

        QueryWrapper<PdrApi> queryWrapper = new QueryWrapper<>();

        if (pdrApiIdList.size() > 0) {
            queryWrapper.notIn("id", pdrApiIdList);
        }

        if (StrUtils.isNotBlank(dto.getName())) {
            queryWrapper.like("name", dto.getName());
        }
        if (StrUtils.isNotBlank(dto.getCode())) {
            queryWrapper.like("code", dto.getCode());
        }
        if (dto.getPdrProjectId() != null) {
            queryWrapper.eq("pdr_project_id", dto.getPdrProjectId());
        }
        if (dto.getEnable() != null) {
            queryWrapper.eq("enable", dto.getEnable());
        }
        if (StrUtils.isNotBlank(dto.getPath())) {
            queryWrapper.like("path", dto.getPath());
        }

        queryWrapper.eq("status", 1);
        queryWrapper.orderByDesc("updated_at");

        return this.baseMapper.selectPage(page, queryWrapper);
    }

    @Override
    public IPage<CsmGrant> getGrantList(GrantListDTO dto) {
        IPage<CsmGrant> page = new Page<>(dto.getPageNo(), dto.getPageSize());
        QueryWrapper<CsmGrant> queryWrapper = new QueryWrapper<>();
        if (dto.getCsmId() != null) {
            queryWrapper.eq("csm_id", dto.getCsmId());
        }
        if (dto.getPdrId() != null) {
            queryWrapper.eq("pdr_id", dto.getPdrId());
        }
        if (StrUtils.isNotBlank(dto.getPdrApiCode())) {
            queryWrapper.like("pdr_api_code", dto.getPdrApiCode());
        }
        queryWrapper.eq("status", 1);
        queryWrapper.orderByDesc("updated_at");
        return this.csmGrantService.getBaseMapper().selectPage(page, queryWrapper);
    }

    @Override
    @Transactional(rollbackFor = BizException.class)
    public PdrApi add(SaveDTO saveDTO) {
        PdrApi pdrApi = this.getByName(null, saveDTO.getName());
        if (pdrApi != null) {
            throw new BizException(10000, "record.key.found");
        }

        PdrProject project = this.getProjectById(saveDTO.getPdrProjectId());
        if (project == null) {
            throw new BizException(10000, "record.found.not");
        }

        pdrApi = this.convertBean.convert(saveDTO, PdrApi.class);

        String code = UUIDUtil.getUUID().replace("-", "").toUpperCase();
        pdrApi.setCode(code);
        pdrApi.setPublishSnap("");
        pdrApi.setLoadIp(project.getLoadIp());
        pdrApi.setProtocol(project.getProtocol());
        pdrApi.setGrayRelease(project.getGrayRelease());
        pdrApi.setLoadBalance(project.getLoadBalance());
        pdrApi.setTranslateProtocol(project.getTranslateProtocol());
        pdrApi.setGlobalGrantParam(saveDTO.getOutParam());
        pdrApi.setKeyUpdateTime(DateUtil.getTimeByMinSecond());
        pdrApi.setHystrixParam("{}");

        this.baseMapper.insert(pdrApi);
        return pdrApi;
    }

    @Override
    public PdrApi update(Integer id, SaveDTO saveDTO) {
        PdrApi pdrApi = this.getById(id);
        if (pdrApi == null) {
            throw new BizException(10000, "record.found.not");
        }
        //若开启判断项目是否开启
        if (pdrApi.getEnable() == 0 && saveDTO.getEnable() == 1) {
            PdrProject project = this.getProjectById(pdrApi.getPdrProjectId());
            if (project == null) {
                throw new BizException(10000, "record.found.not");
            }
            if (project.getEnable() == 0) {
                throw new BizException(10000, "belong.project.not.enable");
            }
        }
        PdrApi pdrApiName = this.getByName(id, saveDTO.getName());
        if (pdrApiName != null) {
            throw new BizException(10000, "record.key.found");
        }
        //判断是否有关键信息
        Boolean keyChange = this.keyInfoChange(saveDTO, pdrApi);
        this.convertBean.convertNotNull(saveDTO, pdrApi, false, true);
//        pdrApi.setHystrixParam(JSONObject.toJSONString(saveDTO.getHystrixParam()));
        if (keyChange) {
            pdrApi.setKeyUpdateTime(DateUtil.getTimeByMinSecond());
        }
        this.baseMapper.updateById(pdrApi);
        return pdrApi;
    }

    private Boolean keyInfoChange(SaveDTO saveDTO, PdrApi pdrApi) {
        if (!pdrApi.getPath().equalsIgnoreCase(saveDTO.getPath())) {
            return true;
        } else if (!pdrApi.getEnable().equals(saveDTO.getEnable())) {
            return true;
        } else if (!pdrApi.getBlackList().equals(saveDTO.getBlackList())) {
            return true;
        } else if (!pdrApi.getQueryType().equalsIgnoreCase(saveDTO.getQueryType())) {
            return true;
        } else return !pdrApi.getPermitsPerSecond().equals(saveDTO.getPermitsPerSecond());
//        else return !pdrApi.getHystrixParam().equalsIgnoreCase(JSONObject.toJSONString(saveDTO.getHystrixParam()));
    }

    @Override
    public PdrApi detail(Integer id) {
        PdrApi pdrApi = this.getById(id);
        if (pdrApi == null) {
            throw new BizException(10000, "record.found.not");
        }
        return pdrApi;
    }

    @Override
    public List<PdrApi> getListByIds(List<Integer> ids) {
        QueryWrapper<PdrApi> queryWrapper = new QueryWrapper<>();
        queryWrapper.in("id", ids);
        queryWrapper.eq("status", 1);
        return this.baseMapper.selectList(queryWrapper);
    }

    @Override
    public List<CsmProject> getCsmByPdrApiId(Integer id) {
        //获取授权列表
        List<CsmGrant> csmGrantList = this.csmGrantService.getGrantListByPdrApiId(id);
        List<Integer> projectIdList = csmGrantList.stream().map(CsmGrant::getCsmId).collect(Collectors.toList());
        List<CsmProject> csmProjectList = new ArrayList<>();
        if (projectIdList.size() > 0) {
            csmProjectList = this.csmProjectService.getCsmByIds(projectIdList);
        }
        return csmProjectList;
    }

    @Override
    public WriteDTO makeApiDoc(Integer csmId) {
        WriteDTO writeDTO = new WriteDTO();
        CsmProject csmProject = this.csmProjectService.getCsmById(csmId);
        if (csmProject == null) {
            throw new BizException(10000, "record.found.not");
        }
        //获取所有授权列表
        List<CsmGrant> csmGrantList = this.csmGrantService.getGrantListByCsmId(csmId);
        //获取所有接口
        List<Integer> pdrApiIdList = csmGrantList.stream().map(CsmGrant::getPdrApiId).collect(Collectors.toList());

        List<ApiInfo> apiInfoList = new ArrayList<>();
        if (pdrApiIdList.size() > 0) {
            List<PdrApi> pdrApiList = this.getListByIds(pdrApiIdList);
            pdrApiList.sort(Comparator.comparing(PdrApi::getPdrProjectId));
            List<Integer> pdrIdList = pdrApiList.stream().map(PdrApi::getPdrProjectId).collect(Collectors.toList());
            List<PdrProject> pdrProjectList = this.projectService.getPdrListByIds(pdrIdList);
            Map<Integer, PdrProject> projectMap = pdrProjectList.stream().collect(Collectors.toMap(PdrProject::getId, pdr -> pdr));
            int length = pdrApiList.size();
            for (int i = 0; i < length; i++) {
                PdrApi pdrApi = pdrApiList.get(i);
                PdrProject project = projectMap.getOrDefault(pdrApi.getPdrProjectId(), null);
                ApiInfo apiInfo = new ApiInfo();
                apiInfo.setIndex(i + 1);
                apiInfo.setApiName(pdrApi.getName());
                apiInfo.setApiDesc(pdrApi.getDescription());
                apiInfo.setApiCode(pdrApi.getCode());
                apiInfo.setApiQueryType(pdrApi.getQueryType());
                apiInfo.setApiDataType(pdrApi.getTranslateProtocol());
                apiInfo.setProjectName(project.getName());
                apiInfo.setServiceType(project.getProtocol());
                apiInfo.setIn(this.generatorApiIn(pdrApi.getInParam()));
                apiInfo.setOut(this.generatorApiOut(pdrApi.getOutParam()));
                apiInfoList.add(apiInfo);
            }
        }

        JSONObject apiDoc = new JSONObject();
        apiDoc.put("title", csmProject.getName());
        apiDoc.put("apiInfoList", apiInfoList);

        writeDTO.setFileName(String.format("%s_%s.doc", csmProject.getName(), DateUtil.getTimeByMinSecond()));
        writeDTO.setData(apiDoc);

        return writeDTO;
    }

    private List<ApiIn> generatorApiIn(String inParam) {
        List<ApiIn> apiInList = new ArrayList<>();
        ApiIn apiInHeader = new ApiIn();
        apiInHeader.setName("参数名");
        apiInHeader.setDesc("参数描述");
        apiInHeader.setType("参数类型");
        apiInHeader.setRequire("是否必传");
        apiInHeader.setExample("示例");
        apiInList.add(apiInHeader);
        List<ApiIn> paramList = JSONObject.parseArray(inParam, ApiIn.class);
        if (paramList.size() > 0) {
            apiInList.addAll(paramList);
        }
        return apiInList;
    }

    private List<ApiOut> generatorApiOut(String outParam) {
        List<ApiOut> apiOutList = new ArrayList<>();
        ApiOut apiOutHeader = new ApiOut();
        apiOutHeader.setName("参数名");
        apiOutHeader.setDesc("参数描述");
        apiOutHeader.setType("参数类型");
        apiOutHeader.setExample("示例");
        apiOutList.add(apiOutHeader);
        List<ApiOut> paramList = JSONObject.parseArray(outParam, ApiOut.class);
        if (paramList.size() > 0) {
            apiOutList.addAll(paramList);
        }
        return apiOutList;
    }

    private PdrApi getById(Integer id) {
        QueryWrapper<PdrApi> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", id);
        queryWrapper.eq("status", 1);
        return this.baseMapper.selectOne(queryWrapper);
    }

    private PdrProject getProjectById(Integer id) {
        QueryWrapper<PdrProject> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", id);
        queryWrapper.eq("status", 1);
        return this.projectService.getBaseMapper().selectOne(queryWrapper);
    }

    private PdrApi getByNameOrPath(Integer id, String name, String path) {
        QueryWrapper<PdrApi> queryWrapper = new QueryWrapper<>();
        if (id != null) {
            queryWrapper.ne("id", id);
        }
        queryWrapper.and(wrapper -> wrapper.eq("name", name).or().eq("path", path));
        queryWrapper.eq("status", 1);
        return this.baseMapper.selectOne(queryWrapper);
    }

    private PdrApi getByName(Integer id, String name) {
        QueryWrapper<PdrApi> queryWrapper = new QueryWrapper<>();
        if (id != null) {
            queryWrapper.ne("id", id);
        }
        queryWrapper.eq("name", name);
        queryWrapper.eq("status", 1);
        return this.baseMapper.selectOne(queryWrapper);
    }
}
